
#include "hls_media_h264.h"
#include "string.h"
#include "math.h"
#include <arpa/inet.h>
#include "crc.h"
#include "bitstream.h"
#include <stdlib.h>
#include <stdio.h>

#define SPLITE_DUR 0.2
#define PID_PMT		0x0100
#define PID_AUDIO	0x0101
#define PID_VIDEO  0x0102
#define PES_VIDEO_STREAM 0x1B
#define PES_AUDIO_STREAM 0x04
#define TRANSPORT_STREAM_ID 0x0001
#define PROGRAM_NUMBER 0x0001
/* generate ts_header */
static int generate_ts_header(
		char* out_buf, int out_buf_size, int cont_count, int payload_unit_start, 
		int need_pcr, double fpcr, int pid, int discontiniuty, int payload_size){
	PutBitContext bs;
	long long pcr;
	int stuffing_size = 0;
	int i;
	int adaptation_field = 0;

	if (payload_size < 184 || need_pcr || discontiniuty){
		adaptation_field = 3;
	}
	else{
		adaptation_field = 1;
	}
	if (payload_size == 0)
		adaptation_field = 2;


	init_put_bits(&bs, out_buf, out_buf_size);
	put_bits(&bs, 8, 0x47); //sync byte
	put_bits(&bs, 1, 0x00); //error indicatior
	put_bits(&bs, 1, payload_unit_start);
	put_bits(&bs, 1, 0x00); //transport pget_frame_durationriority
	put_bits(&bs, 13, pid); //pid
	put_bits(&bs, 2, 0x00); //scrambling control
	put_bits(&bs, 2, adaptation_field); //adaptation
	put_bits(&bs, 4, cont_count & 0x0F); //continiuty counter

	if (adaptation_field == 2 || adaptation_field == 3){
		int adaptation_field_length = 0;
		if (discontiniuty || need_pcr || payload_size < 184 - adaptation_field_length)
			++adaptation_field_length;
		if (need_pcr)
			adaptation_field_length += 6;
		if (payload_size < 184 - adaptation_field_length){
			stuffing_size = 184 - 1 - adaptation_field_length - payload_size;
			adaptation_field_length += stuffing_size;
		}

		put_bits(&bs, 8, adaptation_field_length);//adaptation field length
		/////TODO:need to calcuate
		if (adaptation_field_length > 0){
			put_bits(&bs, 1, discontiniuty);//discontinuity indicator
			put_bits(&bs, 1, 0);//random access indicator
			put_bits(&bs, 1, 0);//ES priority indicator
			put_bits(&bs, 1, need_pcr);//PCR flag
			put_bits(&bs, 1, 0x00);//OPCR flag
			put_bits(&bs, 1, 0x00);//splicing point flag
			put_bits(&bs, 1, 0x00);//transport private data flag
			put_bits(&bs, 1, 0x00);//adaptation field extention flag
			if (need_pcr){
				pcr = fpcr * 27000000LL;
				put_bits64(&bs, 33, (pcr / 300LL) & 0x01FFFFFFFFLL);//program clock reference base
				put_bits(&bs, 6, 0x3F);//reserved
				put_bits(&bs, 9, (pcr % 300) & 0x01FFLL);//program clock reference ext
			}
			//here we have to put stuffing byte
			for (i = 0; i < stuffing_size; i++){
				put_bits(&bs, 8, 0xFF);
			}
		}
	}

	flush_put_bits(&bs);
	return put_bits_count(&bs) / 8;
}

static int generate_pes_header(char* out_buf, int out_buf_size, int data_size, double fpts, double fdts, int es_id){
	PutBitContext bs;

	init_put_bits(&bs, out_buf, out_buf_size);
	int pts_dts_length = 5;
	long long pts;
	long long dts;

	pts = (fpts + 0.300) * 90000LL;//90KHZ, pts/90K = s
	dts = (fdts + 0.300) * 90000LL;

	if (abs(pts - dts) > 10)
		pts_dts_length += 5;

	//pes packet start
	put_bits(&bs, 24,0x0000001);// pes packet start code

	put_bits(&bs, 8,	es_id);					// stream id
	if (es_id >= 0xE0)
		put_bits(&bs, 16,	0);	// pes packet length
	else
		put_bits(&bs, 16,	data_size + 3 + pts_dts_length);	// pes packet length


	put_bits(&bs, 2, 0x02);			// have to be '10b'
	put_bits(&bs, 2, 0x00);			// pes scrambling control
	put_bits(&bs, 1, 0x00);			// pes priority
	put_bits(&bs, 1, 0x01);			// data alignment
	put_bits(&bs, 1, 0x00);			// copyright
	put_bits(&bs, 1, 0x00);			// original or copy

	if (pts_dts_length > 5){
		put_bits(&bs, 2, 0x03);			// pts/dts flags we have only pts
	}
	else{
		put_bits(&bs, 2, 0x02);
	}
	put_bits(&bs, 1, 0x00);			// escr flag
	put_bits(&bs, 1, 0x00);			// es rate flag
	put_bits(&bs, 1, 0x00);			// dsm trick mode flag
	put_bits(&bs, 1, 0x00);			// additional copy info flag
	put_bits(&bs, 1, 0x00);			// pes crc flag
	put_bits(&bs, 1, 0x00);			// pes extention flag
	put_bits(&bs, 8, pts_dts_length);			// pes headder data length

	if (pts_dts_length > 5){
		put_bits(&bs, 4,  0x03);// have to be '0011b'
		put_bits(&bs, 3,  (pts >> 30) & 0x7);// pts[32..30]
		put_bits(&bs, 1,  0x01);// marker bit
		put_bits(&bs, 15, (pts >> 15) & 0x7FFF);// pts[29..15]
		put_bits(&bs, 1,  0x01);// marker bit
		put_bits(&bs, 15, pts & 0x7FFF);// pts[14..0]
		put_bits(&bs, 1,  0x01);// marker bit

		put_bits(&bs, 4,  0x01);// have to be '0011b'
		put_bits(&bs, 3,  (dts >> 30) & 0x7);// pts[32..30]
		put_bits(&bs, 1,  0x01);// marker bit
		put_bits(&bs, 15, (dts >> 15) & 0x7FFF);// pts[29..15]
		put_bits(&bs, 1,  0x01);// marker bit
		put_bits(&bs, 15, dts & 0x7FFF);// pts[14..0]
		put_bits(&bs, 1,  0x01);// marker bit

	}else{
		put_bits(&bs, 4,  0x02);// have to be '0010b'

		put_bits(&bs, 3,  (pts >> 30) & 0x7);// pts[32..30]
		put_bits(&bs, 1,  0x01);// marker bit
		put_bits(&bs, 15, (pts >> 15) & 0x7FFF);// pts[29..15]
		put_bits(&bs, 1,  0x01);// marker bit
		put_bits(&bs, 15, pts & 0x7FFF);// pts[14..0]
		put_bits(&bs, 1,  0x01);// marker bit
	}


	flush_put_bits(&bs);
	return put_bits_count(&bs)/8;

}

void pack_data(char* ts_buf, int* frame_count, int* cc, double pts, double dts, int es_id, int pid, char* data, int frame_size, int pcr_pid){
	char pes_header_buffer[128];
	char ts_header_buffer[188];

	int pes_header_size = 0;
	int ts_header_size;
	int pos = 0;
	if (frame_size > 0){
		pes_header_size = generate_pes_header(pes_header_buffer, sizeof(pes_header_buffer), frame_size, pts, dts, es_id);
	}
	pos = 0;

	if ( pcr_pid){
		ts_header_size = generate_ts_header(ts_header_buffer, sizeof(ts_header_buffer), cc[0], 1,
												1, dts, pid, 0, frame_size + pes_header_size);
	}else{
		ts_header_size = generate_ts_header(ts_header_buffer, sizeof(ts_header_buffer), cc[0], 1,
												0, 0, pid, 0, frame_size + pes_header_size);
	}

	memcpy(ts_buf + 188 * frame_count[0], ts_header_buffer, ts_header_size);
	memcpy(ts_buf + 188 * frame_count[0] + ts_header_size, pes_header_buffer, pes_header_size);
	memcpy(ts_buf + 188 * frame_count[0] + ts_header_size + pes_header_size, data, 188 - ts_header_size - pes_header_size);

	++frame_count[0];
	++cc[0];

	pos += 188 - ts_header_size - pes_header_size;

	while (pos < frame_size){

//		ts_header_size = generate_ts_header(ts_header_buffer, sizeof(ts_header_buffer), cc[0],
	//										0, 1, start_time + pos / 8000.0, pid, 0, frame_size - pos);
		ts_header_size = generate_ts_header(ts_header_buffer, sizeof(ts_header_buffer), cc[0],
													0, 0, 0, pid, 0, frame_size - pos);

		memcpy(ts_buf + 188 * frame_count[0], ts_header_buffer, ts_header_size);
		memcpy(ts_buf + 188 * frame_count[0] + ts_header_size , data + pos, 188 - ts_header_size);

		pos += 188 - ts_header_size;
		++frame_count[0];
		++cc[0];
	}
}

int put_pat(char* buf,  int* pat_cc)
{
	PutBitContext bs;
	unsigned char tmp[188] = { 0xFF };

	int pat_size = 0;
	int header_size = 0;

	init_put_bits(&bs, tmp, 188);
	put_bits(&bs, 8, 0);//table offset---调整字节
	put_bits(&bs, 8, 0);//table id PAT

	put_bits(&bs, 1, 0x01);//section syntax indicator
	put_bits(&bs, 1, 0);//have to be 0
	put_bits(&bs, 2, 0x03);//reserved
	put_bits(&bs, 12, (16 + 2 + 5 + 1 + 8 + 8 + 16 + 3 + 13 + 32) / 8);//section length
	put_bits(&bs, 16, 0x0001);//transport stream id
	put_bits(&bs, 2, 0x03);//reserved
	put_bits(&bs, 5, 0x00);//version number
	put_bits(&bs, 1, 0x01);//current next indicator
	put_bits(&bs, 8, 0x00);//section number
	put_bits(&bs, 8, 0x00);//last section number


	put_bits(&bs, 16, 0x0001);//program number
	put_bits(&bs, 3, 0x07);//reserved
	put_bits(&bs, 13, 0x0100);//program map id

	put_bits(&bs, 32, htonl(crc(crc_get_table(CRC_32_IEEE), -1, (const uint8_t*)(&tmp[0]) + 1, put_bits_count(&bs) / 8 - 1)));

	pat_size = put_bits_count(&bs) / 8;// 16
	memset(&tmp[pat_size], 0xff, 188 - pat_size);

	header_size = generate_ts_header(buf, 188, pat_cc[0], 1, 0, 0, 0, 0, 184);//header fixed size 4bytes
	memcpy(buf + header_size, tmp, 184);
	++pat_cc[0];
	return 188;
}

int put_pmt(char* buf, int* pmt_cc)//surppose just video data
{
	PutBitContext bs;
	unsigned char tmp[188];
	int pmt_size = 0;
	int header_size = 0;

	init_put_bits(&bs, tmp, 188);

	put_bits(&bs, 8, 0);//table offset
	put_bits(&bs, 8, 0x02);//table id for PMT
	put_bits(&bs, 1, 0x01);//section syntax indicator
	put_bits(&bs, 1, 0x00);//0
	put_bits(&bs, 2, 0x03);//reserved
	put_bits(&bs, 12, (16 + 2 + 5 + 1 + 8 + 8 + 16 + 16 + (8 + 16 + 16)*1 + 32) / 8);//section length
	put_bits(&bs, 16, 0x0001);//program number
	put_bits(&bs, 2, 0x03);//reserved
	put_bits(&bs, 5, 0x00);//version number
	put_bits(&bs, 1, 0x01);//current next indicator
	put_bits(&bs, 8, 0x00);//section number
	put_bits(&bs, 8, 0x00);//last section number

	put_bits(&bs, 3, 0x07);//reserved
	put_bits(&bs, 13, 0x0100 + 1);//PCR PID

	put_bits(&bs, 4, 0x0F);//reserved
	put_bits(&bs, 12, 0x0000);//program info length

	/* just one */
	put_bits(&bs, 8, 0x1B);//h264
	put_bits(&bs, 3, 0x07);//reserved
	put_bits(&bs, 13, 0x0100 + 1);//elementary PID
	put_bits(&bs, 4, 0x0F);//reserved
	put_bits(&bs, 12, 0x0000);//es info lenght

	put_bits(&bs, 32, htonl(crc(crc_get_table(CRC_32_IEEE), -1, (const uint8_t*)(&tmp[0]) + 1, put_bits_count(&bs) / 8 - 1)));//here we calculate CRC of section without first Pointer byte

	pmt_size = put_bits_count(&bs) / 8;

	memset(&tmp[pmt_size], 0xff, 188 - pmt_size);

	header_size = generate_ts_header(buf, 188, pmt_cc[0], 1, 0, 0, 0x0100, 0, 184);//fixed size 4bytes
	memcpy(buf + header_size, tmp, 184);
	++pmt_cc[0];
	return 188;
}

int put_data_frame(char* buf, char* frame_data, int frame_size){

	int fc = 0;
	int data_buf_size = 0;
	double pts;
	double dts;
	static long long g_frame_cnt = 0;
	static int ts_cnt = 0;

	int es_id = 0xE0;

	pts = dts = 0.04 * g_frame_cnt;//consier overflow

	pack_data(buf, &fc, &ts_cnt, pts, dts, es_id, 0x0100 + 1,
		frame_data, frame_size, 1);

	g_frame_cnt++;

	return fc * 188;//return number of written bytes
}

static int write_m3u8_head_block(char *text, int version, int target_duration, int64_t sequence)
{
	int total_cnt = 0;
	int cnt = 0;
	char *buf = text;
	cnt = sprintf(buf, "#EXTM3U\n");
	buf += cnt;
	total_cnt += cnt;
	cnt = sprintf(buf, "#EXT-X-VERSION:%d\n", version);
	buf += cnt;
	total_cnt += cnt;
	//sprintf(buf, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");//allow cache
	cnt = sprintf(buf, "#EXT-X-TARGETDURATION:%d\n", target_duration);
	buf += cnt;
	total_cnt += cnt;
	cnt = sprintf(buf, "#EXT-X-MEDIA-SEQUENCE:%ld\n", sequence);//
	total_cnt += cnt;
	return total_cnt;
}
static int inited =0;
void generate_m3u8_file(char *file_url, int64_t sequence, double duration, char *ts_name)
{
	char file_name[1024];
	char *file_format = "m3u8";
	char temp_text[4096];

	//carmer_101_0.ts
	sprintf(file_name, "%s.%s", file_url, file_format);
	int cnt = 0;
	if(0 == inited)
	{
		inited = 1;
		cnt += write_m3u8_head_block(temp_text, 3, 2, sequence);//target_duration is fixed, setting
	}
	cnt += sprintf(temp_text + cnt, "#EXTINF:%f,\n", duration);
	cnt += sprintf(temp_text + cnt, "%s\n", ts_name);
	FILE *file = fopen(file_name, "a+");
	if (file){
		fwrite(temp_text, 1, cnt, file);
		fclose(file);
	}
}

void generate_m3u8_end(char *file_url)
{
	char file_name[1024];
	char *file_format = "m3u8";
	char temp_text[4096];

	//carmer_101_0.ts
	sprintf(file_name, "%s.%s", file_url, file_format);

	sprintf(temp_text , "#EXT-X-ENDLIST\n");
	FILE *file = fopen(file_name, "a+");
	if (file){
		fwrite(temp_text, 1, sizeof(temp_text), file);
		fclose(file);
	}
}

#define MAX_FRAME_SIZE 1000000
#define FRAM_PER_SEG 5
#define FRAME_NUM (FRAM_PER_SEG*1000)
struct g_frames_st{
	char data[MAX_FRAME_SIZE];
	int size;
}g_frames[FRAME_NUM];

int h264_find_nalu(struct g_frames_st *gf)
{
	FILE *file = fopen("video/carmer_101.h264", "rb");
	int cmp_cnt;
	char c;
	int i=0;
	int pos=0;
	int frame_cnt;
	cmp_cnt=0;
	while(!feof(file) && i<FRAME_NUM)
	{
		c = getc(file);
		if( 0x00 == c)
		{
			if(cmp_cnt<2)
			{
				cmp_cnt++;
			}
		}
		else if(2 == cmp_cnt && 0x01 == c)
		{
			//printf("find flag!id=%d,data size = %d\n",i,pos);
			if(i>0)
			{
				gf[i-1].size=pos;
			}
			i++;
			gf[i-1].data[0]=0x00;
			gf[i-1].data[1]=0x00;
			gf[i-1].data[2]=0x01;
			/*
			gf[i-1].data[3]=0x01;
			gf[i-1].data[4]=0x09;
			gf[i-1].data[5]=0xf0;
			*/
			//const uint8_t cUAudData[6] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0 };
			pos=3;
			cmp_cnt=0;
		}
		else
		{
			if(pos==4&&c==0x09)
			{
				printf("11111111111111111\n");
			}
			if(i>0)
			{
				if(1==cmp_cnt)
				{
					gf[i-1].data[pos]=0x00;
					pos++;
				}
				else if (2==cmp_cnt)
				{
					gf[i-1].data[pos]=0x00;
					pos++;
					gf[i-1].data[pos]=0x00;
					pos++;
				}
				gf[i-1].data[pos]=c;
			}
			cmp_cnt=0;
			pos++;
		}
	}
	frame_cnt = i;
	return frame_cnt;
}

void generat_ts_file()
{
	int i;
	int j;
	int lead_track;
	int video_track;
	int total_size = 0;
	int frame_num=0;
//	long long buffer_size = 4 * total_size + 20000;
	long long buffer_size = 4 * total_size + MAX_FRAME_SIZE * FRAM_PER_SEG;
//	char *pDataBuffer = new char[buffer_size];
	char *pDataBuffer = malloc(buffer_size);
	int pos ,  pat_cc , pmt_cc ;
	char tmp[1024];
	char ts_name[1024];
	static long cnt = 0;
	//	frag/carmer_101.mp4_0.ts
	frame_num=h264_find_nalu(g_frames);
	printf("frame_num=%d\n",frame_num);
	for(j=0;j<frame_num;)
	{
		pos = 0,  pat_cc = 0, pmt_cc = 0;
		sprintf(ts_name, "carmer_101_%ld.ts", cnt);
		sprintf(tmp, "frag/carmer_101_%ld.ts", cnt);

		pos += put_pat(pDataBuffer + pos, &pat_cc);//pat packet
		pos += put_pmt(pDataBuffer + pos, &pmt_cc);//pmt header

		/* write 25 frames */
		for (i=0; i<FRAM_PER_SEG; i++)
		{
			pos += put_data_frame(pDataBuffer + pos, (char *)(g_frames[j].data), g_frames[j].size);
			j++;
		}

		FILE *file = fopen(tmp, "wb");
		if (file){
			fwrite(pDataBuffer, 1, pos, file);
			fclose(file);
		}
		/* write m3u8 */
//		int64_t duration = g_frames.rbegin()->time_stamp() - g_frames.begin()->time_stamp();
//		double fDuration = (double)(duration / 90000);

		generate_m3u8_file("frag/carmer_101", cnt, SPLITE_DUR, ts_name);
		cnt++;
	}
	generate_m3u8_end("frag/carmer_101");
	free(pDataBuffer);
	return;
}

int main()
{
	/*
	track_t track;
	track.codec = H264_VIDEO;
	track.sample_rate = 25;
	*/
	generat_ts_file();
	return 0;
}


